﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Clandestine.Audio.Decoders
{
	[DecoderHandlesFileType(new string[] { "wav" })]
	internal class WaveDecoder : Decoder
	{
		UInt32 samplesLen;
		UInt32 sampleRate;
		
		FileStream fs;
		BinaryReader brr;
		
		bool stereo;
		
		public override bool TryStartDecode(string filename)
		{
			// Note: This is a VERY primitive wave file decoder, and will accept only a subset of possible legal files!
			// Really, we need to outfit this biznitch with a proper RIFF dechunker.
			// This said it'll do 'normal' .wavs, more or less.  Not that there's such a thing as a normal .wav.
			fs = File.OpenRead(filename);
			BinaryReader br = new BinaryReader(fs);

			if (!vaeraski(br, "RIFF"))
				return false;
			br.ReadInt32();
			if (!vaeraski(br, "WAVE"))
				return false;
			
			if (!vaeraski(br, "fmt "))
				return false;
			br.ReadInt32();
			
			UInt16 compressionCode = br.ReadUInt16();
			
			if (compressionCode != 1) // PCM/Uncompressed
				return false;
			
			UInt16 channelCount = br.ReadUInt16();
			
			stereo = (channelCount != 1);
			
			if (channelCount > 2 || channelCount == 0)
				return false;
			
			sampleRate = br.ReadUInt32();
			
			//if (sampleRate != 44100)
			//	return false;
			
			UInt32 avgBytesPerSec = br.ReadUInt32();
			
			UInt16 blockAlign = br.ReadUInt16();
			UInt16 sigBitsPerSample = br.ReadUInt16();
			
			if (sigBitsPerSample != 16)
				return false;
			
			//UInt16 extraFmtBytes = br.ReadUInt16();
			
			//br.ReadBytes(extraFmtBytes);
			
			
			// 'Wave files usually contain only one data chunk, but they may contain more than one'
			// /me shrugs
			if (!vaeraski(br, "data"))
				return false;
			
			samplesLen = br.ReadUInt32();
			
			// 2 bytes/sample, 44100 samples/sec, * 2 channels
			//Log.i(samplesLen.ToString());
			length = new TimeSpan((long)(TimeSpan.TicksPerSecond * ((double)samplesLen / ((stereo ? 2 : 1) * 44100 * 2))));
			
			//Log.i("Wav decoder: I think the .wav is " + length.TotalSeconds.ToString() + " sechs long. olol.");
			brr = br;
			return true;
		}
		
		private bool vaeraski(BinaryReader br, string str)
		{
			foreach (char c in str)
				if (((byte)c) != br.ReadByte())
					return false;
			return true;
		}

		public override void EndDecode()
		{
			brr.Close();
			
		}

		public override Channels Channels
		{
			get { return (stereo ? Channels.Stereo : Channels.Mono); }
		}

		private TimeSpan length;
		public override TimeSpan Length
		{
			get { return length; }
		}

		/*public override byte[] DecodeWholeFilePCM16bit44100hz()
		{
			return brr.ReadBytes((int)samplesLen);
		}*/
		
		#region implemented abstract members of Clandestine.Audio.Decoder
		public override void Rewind ()
		{
			fs.Seek(44, SeekOrigin.Begin);
		}
		
		
		public override int Rate
		{
			get { return (int)this.sampleRate; }
		}
		
		
		public override bool EndOfStream
		{
			get { return (fs.Position >= fs.Length - 1); }
		}
		
		
		public override byte[] DecodeChunkPCM16bit ()
		{
			return brr.ReadBytes((int) Math.Min(fs.Length - fs.Position - 1, StreamingBuffer.PreferredDecodeSize));
		}
		
		#endregion
	}
}
